Node.js dynamic content
https://levelup.gitconnected.com/render-dynamic-content-in-nodejs-using-templates-a58cae681148
Must Watch!
MustWatch
Learn how to render UI from Node.js by building real applications
In this article, we will see how we can render the front-end using Node.js which is very helpful to display static or dynamic content.
What are templates?
Templates allow us to render dynamic content inside html enabling us to use loops, if conditions, and other features. It allows us to inject dynamic data at runtime.
We can build an entire front-end using just templating in Node.js without using front-end libraries.
We can generate dynamic html and save the dynamically generated content in a file. There are a variety of template libraries.
Some of the popular options are handlebars
, pug/jade
, EJS
etc.
We will be using express.js
to handle routing and EJS
(Embedded JavaScript Templates) which is a very popular library with around 6 million weekly downloads.
One of the reasons EJS
is so popular is because it allows writing JavaScript code directly inside the template so we can use loops
, if conditions
, and all things provided by JavaScript. Other template libraries use their own syntax and do not directly write JavaScript.
Create a new folder with the name templating-nodejs
and navigate to that folder from terminal and initialize package.json
by running following command:
npm init -y
Install express
and ejs
by running:
npm install express@4.17.1 ejs@3.1.2
Install nodemon
as dev dependency by running:
npm install nodemon@2.0.3 --save-dev
Create a new file index.js
and add the following code inside it:
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Welcome to EJS world!
");
});
app.listen(3000, () => {
console.log("server started on port 3000");
});
Add a start script inside package.json
"start": "nodemon index.js"
Now start the express server by executing the npm start
command from the command line:
You can access the application now by navigating to http://localhost:3000/
Now let’s add the ejs functionality into the application.
Create a new views
folder and add an index.ejs
file inside it with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Index page</title>
</head>
<body>
<h1>This is index page!</h1>
</body>
</html>
index.ejs
To render this file, we need to add some code in our express app.
Specify the render engine by adding the following code in index.js
file
app.set("view engine", "ejs");
And to render the index.ejs
, we need to use the render
method of the res
object and pass the name of the ejs file without providing the .ejs extension.
Express will automatically search for the file with .ejs extension inside the default views
directory
app.get("/", (req, res) => {
res.render("index"); // index refers to index.ejs
});
So now if you navigate to http://localhost:3000/, you will see the content from index.ejs
being rendered
Add CSS and JavaScript support
Now, we will see how to add css
and JavaScript
to our template.
Create a new folder with the name public
and add a css
folder inside it
Add styles.css
inside it with the following content
Your folder structure will look like this now
folder structure
Add a link to the css
file inside the views/index.ejs
file
<link rel="stylesheet" href="/css/styles.css" />
index.ejs
Also add following code in index.js
so we can load all the html
,css
and javacript
files from the public
folder.
app.use(express.static(path.join(__dirname, "public")));
If you check the application now, you will see that the CSS is getting applied
To add JavaScript files, create a new js
folder inside public
folder and add script.js
inside it with a simple console.log
statement
console.log("This is coming from script.js");
Include the script.js
inside views/index.ejs
file before the body end tag
If you save the file, you will see that nodemon
is not restarted automatically, so our changes are not reflected in the application.
This is because we specified to watch only index.js
to nodemon
in package.json
.
To fix this, open package.json
and change the start script from
"start": "nodemon index.js"
to
"start": "nodemon -e js,ejs"
Here, we informed nodemon
to reload the changes if there is any change in .js
or .ejs
file by passing an -e
flag.
Now restart your nodemon
script by re-running the npm start
command from the terminal, and now if you save the views/index.ejs
file, you will see that the nodemon
restarts automatically on save.
Now, navigate to http://localhost:3000/ and refresh the page. You will see the log from script.js
printed in console:
Better organize files
EJS also allows us to split a single file into separate files so we can have common header and footer which we can display on all pages.
To include a file inside another file we use the following syntax
<%- include('header.ejs') %>
Here, the <%
and minus
sign is used to include the file.
Now we will split our index.js
code into header.ejs
, footer.ejs
, and content.ejs
and include them into our index.ejs
<h1>This is index page!</h1>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Index page</title>
<link rel="stylesheet" href="/css/styles.css" />
</head>
<body>
<script src="/js/script.js"></script>
</body>
</html>
footer.ejs
<%- include('header.ejs') %>
<%- include('content.ejs') %>
<%- include('footer.ejs') %>
index.ejs
Note that we don’t have closing body
and html
tag inside header.ejs
because we have added that in footer.ejs
If you have some formatter installed in VS Code and it’s automatically adding the missing tag on save, you can remove that closing tag and save the file without formatting as shown below
Press Control + Shift + P
or Command + Shift + P (Mac)
to open command palette and type save
and then select “File: Save without Formatting
” option
Disable auto formatting
Now we will create a simple login application using ejs templating where we will display the success message on login and failure message for login failure.
Open content.ejs
and add following code inside it
content.ejs
Here, we are allowing users to enter name and password
and once user submits the form, we are calling the /login
route added in index.js
as specified in the form tag
<form action="/login" method="post">
Create a new file success.ejs
inside views
folder and add following content
success.ejs
Here, we are displaying the logged in user username and success message.
To display any value in the ejs
file, we use the variable name between <%=
and %>
<%=
is used to display any value by converting it to a string.
Create a new file failure.ejs
inside views
folder and add the following content
failure.ejs
Updated styles.css
To parse the input from the form, install the body-parser
npm package by running following command from the terminal
npm install body-parser@1.19.0
and to inform the express to parse the input as JSON object import that in index.js
const bodyParser = require("body-parser");const app = express();app.use(bodyParser.urlencoded({ extended: false }));
Now, the form data will be available in req.body
object inside the /login
route
app.post("/login", (req, res) => {
const { name, password } = req.body;
if (name === "admin" && password === "admin") {
res.render("success", {
username: name,
});
} else {
res.render("failure");
}
});
login route inside index.js
Here, we check the login data. If name
and password
matches, then we display the success.ejb
template, otherwise display the failure.ejb
.
Note that to pass data to the success.ejb
template, we are passing as an object in second argument to the render method so it will be available inside that template.
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.get("/", (req, res) => {
res.render("index");
});
app.post("/login", (req, res) => {
const { name, password } = req.body;
if (name === "admin" && password === "admin") {
res.render("success", {
username: name,
});
} else {
res.render("failure");
}
});
app.listen(3000, () => {
console.log("server started on port 3000");
});
Updated index.js
Now your application will work as shown below
Login ApplicationBuilding Github repository listing App using EJS template
Now we will build an app that shows the repositories of any user passed as a query string in the URL.
Install axios
from the command line by running
npm install axios@0.19.2
Inside index.js
add the following route
app.get("/repos", async (req, res) => {
const username = req.query.username || "myogeshchavan97";
try {
const result = await axios.get(
`https://api.github.com/users/${username}/repos`
);
const repos = result.data.map((repo) => ({
name: repo.name,
url: repo.html_url,
description: repo.description,
}));
res.render("repos", {
repos
});
} catch (error) {
console.log(error);
res.status(400).send("Error while getting list of repositories");
}
});
Here, we are getting the username
from query string so the URL will be like this http://localhost:3000/repos?username=your_github_username
If no username
is specified, it will take the default value provided in the route.
Then we are taking the repository data and sending it to the repos.ejb
template, we are looping through it and displaying the result.
const express = require("express");
const axios = require("axios");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.get("/", (req, res) => {
res.render("index");
});
app.post("/login", (req, res) => {
const { name, password } = req.body;
if (name === "admin" && password === "admin") {
res.render("success", {
username: name,
});
} else {
res.render("failure");
}
});
app.get("/repos", async (req, res) => {
const username = req.query.username || "myogeshchavan97";
try {
const result = await axios.get(
`https://api.github.com/users/${username}/repos`
);
const repos = result.data.map((repo) => ({
name: repo.name,
url: repo.html_url,
description: repo.description,
}));
res.render("repos", {
repos
});
} catch (error) {
console.log(error);
res.status(400).send("Error while getting list of repositories");
}
});
app.listen(3000, () => {
console.log("server started on port 3000");
});
updated index.js
Create a new repos.ejs
file inside the views
folder with following code
<%- include('header.ejs') %>
<ul class="repos">
<% repos.forEach(function(repo) { %>
<li class="repo">
<h2><a href="<%=repo.url%>" class="repo-title"><%=repo.name%></a></h2>
<p class="repo-description"><%=repo.description%></p>
</li>
<% })%>
</ul>
<%- include('footer.ejs') %>
repos.ejs
As you can see here, we have written JavaScript code in between <%
and %>
. In this case we have added a JavaScript forEach
loop to iterate through the repos object passed from index.js
.
Now you can see the repositories of provided GitHub user:
Repository List ApplicationCustomize default template directory
By default, EJS will look for .ejs
files inside the views
folder in your project and if it does not find it, it will throw an error. We can also provide our custom path for views
directory.
So if we want to store a views
folder inside the templates
folder, we can specify the path by setting it to express app.
const path = require("path");const viewsDirPath = path.join(__dirname, "templates", "views");app.set("views", viewsDirPath);
You can find the complete source code for this application HERE and live demo HERE